/*
 * Copyright 2011 Raimon Rodriguez
 *
 * This file is part of sgfx.
 *
 * sgfx is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * sgfx is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with sgfx. If not, see <http://www.gnu.org/licenses/>.
 */

package com.googlecode.sgfx.controller;

import ca.odell.glazedlists.event.ListEventListener;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.event.ListSelectionListener;

/**
 * This controller handles the logic of a double list component. When no longer required, an
 * instance can be detached from the components by calling the method detach.
 * 
 * @author raimon.rodriguez
 */
public final class DoubleListController {
    private Set<Registration> registrations;

    
    DoubleListController() {}


    /**
     * Unregisters this double list controller from all the components provided to the
     * corresponding builder.
     * Calling this method a second time is a no op.
     */
    public void detach() {
        if (getRegistrationsLazy().isEmpty()) return;
        
        for (Registration tRegistration : getRegistrationsLazy()) {
            tRegistration.remove();
        }

        getRegistrationsLazy().clear();
        registrations = Collections.emptySet();
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this).add("registrations", registrations).toString();
    }
    
    void register(JButton pButton, ActionListener pListener) {
        add(new ButtonActionListenerRegistration(pButton, pListener));
    }

    void register(JComponent pComponent, KeyListener pListener) {
        add(new ComponentKeyListenerRegistration(pComponent, pListener));
    }

    void register(SelectionEventList<?> pSelectionEventList, ListSelectionListener pListener) {
        add(new ListSelectionListenerRegistration(pSelectionEventList, pListener));
    }

    <E> void register(SelectionEventList<E> pSelectionEventList, ListEventListener<E> pListener) {
        add(new EventListListenerRegistration<E>(pSelectionEventList, pListener));
    }

    Set<Registration> getRegistrations() {
        return Collections.unmodifiableSet(getRegistrationsLazy());
    }

    private void add(Registration pRegistration) {
        try {
            getRegistrationsLazy().add(pRegistration);
        } catch (UnsupportedOperationException uoe) {
            throw new IllegalStateException("Can't register anything after invoking detach()", uoe);
        }
    }

    private Set<Registration> getRegistrationsLazy() {
        if (registrations == null) {
            registrations = new HashSet<Registration>();
        }
        return registrations;
    }

    
    static abstract class Registration<E, L extends EventListener> {
        E observable;
        L listener;

        Registration(E pObservable, L pListener) {
            observable = Preconditions.checkNotNull(pObservable, "pObservable");
            listener = Preconditions.checkNotNull(pListener, "pListener");
        }

        @Override
        public String toString() {
            return Objects.toStringHelper(this).add("observable", observable)
                    .add("listener", listener).toString();
        }

        abstract void remove();
    }

    private static class ButtonActionListenerRegistration
            extends Registration<JButton, ActionListener> {

        ButtonActionListenerRegistration(JButton pButton, ActionListener pListener) {
            super(pButton, pListener);
            observable.addActionListener(listener);
        }

        void remove() {
            observable.removeActionListener(listener);
        }
    }

    private static class ComponentKeyListenerRegistration
            extends Registration<JComponent, KeyListener> {

        ComponentKeyListenerRegistration(JComponent pComponent, KeyListener pListener) {
            super(pComponent, pListener);
            observable.addKeyListener(listener);
        }

        void remove() {
            observable.removeKeyListener(listener);
        }
    }

    private static class ListSelectionListenerRegistration
            extends Registration<SelectionEventList<?>, ListSelectionListener> {

        ListSelectionListenerRegistration(SelectionEventList<?> pSelectionList,
                ListSelectionListener pListener) {
            super(pSelectionList, pListener);
            observable.addListSelectionListener(listener);
        }

        void remove() {
            observable.removeListSelectionListener(listener);
        }
    }

    private static class EventListListenerRegistration<E>
            extends Registration<SelectionEventList<E>, ListEventListener<E>> {

        EventListListenerRegistration(SelectionEventList<E> pList, ListEventListener<E> pListener) {
            super(pList, pListener);
            observable.addListEventListener(listener);
        }

        void remove() {
            observable.removeListEventListener(listener);
        }
    }
}
